今天不寫 Code,前 19 篇一股腦去改程式,寫測試。發現好像有一些基本觀念沒有介紹,就被跳過了。趁這段時間來補上資訊,否則又怕直接水到 Day 30,又留下懸念。
val 或 var,class 等,可以加上一些魔法語句來防止閒雜人等存取。前幾天有看到:
var name: String
private set
這個 private 就是術語之一,代表只有這個 class 裡頭 的人,才能存取。
在 Kotlin 之中,預設情形如果不寫,就是 public 的,也就會變成公共財,大家都能用。
還有一個比較特別的是 protected,讓自己的子孫可以存取。從 Code 來看:
open class Car {
protected val brand = "toyota"
private val engine = 1800
}
如果任何人繼承了 Car,則可以存取 brand。
class Rav4 : Car() {
fun main() {
println(brand) // 👈🏼 可以取得 "toyota"
println(engine) // 🚷 錯誤,因為 engine 是 private
}
}
這時如果 Rav4 在其他地方被 new 出來,也無法存取 brand。
fun main() {
val car = Rav4()
car.brand // 🚷 無法,因為不是子孫,無法取得
}
變數的型態,前幾章已經有介紹過呈現真/假使用的—— Boolean,以及整數的大家庭,最常用的:Int 和 Long,以及比較少用到的 Short 和 Byte。當然不要忘記表達文字的—— String 和一個字的 Char。
在數值方面,還有一個家族,就是浮點數(基本上就是有「小數點」的數字),能拿來做更精細的運算。Kotlin 中,浮點數主要有兩個 Type:Float 和 Double。
來看看兩者有什麼異同:
| 資料型態 (Type) | 記憶體大小 (Size) | 浮點數精度 (Precision) | 適用情境舉例 |
|---|---|---|---|
Float (單精度) |
4 Bytes (32 bits) | 約 6 - 7 位有效數字 | 圖形處理 (3D 運算)、 行動裝置遊戲開發 (省記憶體) |
Double (雙精度) |
8 Bytes (64 bits) | 約 15 - 17 位有效數字 | 科學計算、數學公式分析、 大多數程式的預設小數型態 |
Kotlin 很聰明,如果啥都不寫:
val point = 1.5
預設會給 Double,只有加上 f 才會預設是 Float。(e.g. 1.5f)
如果小數點真的太多啦,直接用 Double 就好,以免資訊遺失。
即是 abstract class (中譯應該是:抽象類別),interface 裡頭需要先定義合約裡頭,實作的人需要實作的內容。而 abstract class 則除了合約內容,已經有一些做好的 Function,不用重新實作可以直接用。 abstract class 比較像買房後建商附的裝潢,買房後不用重新裝潢,可以直接使用,但房型已經定義好的房間種類,則需要遵守(不可能廚房變房間吧 🫨)。
Interface 則是照著設計圖去做房間的裝潢即可。
直接來看 Code:
interface MyComputer {
fun boot(): Boolean
fun shutdown(): Boolean
}
任何實作這個 MyComputer interface 的,都需要服從契約—— 實作這兩個 Function。
class EeeePC(private val machine: Machine) : MyComputer {
// 👇🏼 要實作
override fun boot(): Boolean {
return machine.boot();
}
// 👇🏼 要實作
override fun shutdown(): Boolean {
return machine.shutdown()
}
}
不過,如果 MyComputer 都有共同的工作要做,就直接用 abstract class 將重複的行為做定義就好:
// 👇🏼 abstract class 可以 inject
abstract class MyComputer(private val lManager: LanguageManager) {
// 可以留契約
abstract fun boot(): Boolean
abstract fun shutdown(): Boolean
// 可以先建立好行為
fun startTwEnv() {
lManager.switchToZhTw()
boot() // 👈🏼 很厲害的方式是,可以直接去叫契約,行為就看實作方怎麼做
}
}
只要繼承自 MyComputer,就需要實作契約,不過也免費的得到了預先定義好的 Function。
class EeeePC(
lManager: LanguageManager,
private val machine: Machine
) : MyComputer(lManager) {
// 👇🏼 要實作
override fun boot(): Boolean {
return machine.boot();
}
// 👇🏼 要實作
override fun shutdown(): Boolean {
return machine.shutdown()
}
}
如此一來這個 EeeePC 只要被 new 出來後,就可以使用 startTwEnv() 了!
var name: String private set 就是最典型的用法——外面能讀不能改。Car 裡的 brand,Rav4 繼承後拿得到,外部 new 出來的物件卻摸不到。class Rav4 : Car() 的括號代表呼叫父類別建構子;interface 才不用加,兩者別搞混。startTwEnv()),繼承者免費獲得現成行為。startTwEnv() 裡直接叫 boot(),行為由實作方決定——這是抽象類別「定義流程、延遲細節」的厲害之處。